/**********************************************************************************************************************
 * @file    qlmisc.c
 * @author  Queclink Terry.Huang
 * @date    2018-07-04
 * @brief   Miscellaneous, 混杂的, 各种各样的定义以及函数.
 *
 * Copyright (C) 2018 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @attention
 *
 * @usage
 *
 *********************************************************************************************************************/

/* includes ---------------------------------------------------------------------------------------------------------*/
#include "qlibc_log.h"
#include <qlcommon.h>
#include <math.h>
#include <string.h>
#include <ctype.h>

#define low_to_upp(ch)  ((97 <= ch && ch <= 122) ? -32 :0)   //定义宏判断是否是小写字母，如果是则换成大写字母

#define MILE_TO_KILOMETER           ((double)1.852)                  /*1海里=1.852KM*/
#define PI                          ((double)3.1415926535898)        /*圆周率*/
#define EARTH_RADIUS                ((double)6378.1370)              /*地球半径(单位为千米)*/

/* 将角度<==>弧度 */
#define angel_to_rad(angel) ((angel) * PI / 180.0)
#define rad_to_angel(rad)   ((rad) * 180.0 / PI)

char *qlsdk_strtok(char *str, const char *delim)
{
    static char *src = NULL;
    char *p0 = NULL;
    const char *p1 = NULL;

    if (NULL != str) {
        src = str;
    } else if (NULL != src) {
        str = src;
    } else {
        return NULL;
    }

    for (p0 = str; '\0' != *p0; ++p0) {
        for (p1 = delim; '\0' != *p1; ++p1 ) {
            if (*p0 == *p1) {
                *p0 = '\0';
                src = p0 + 1;

                return str;
            }
        }
    }
    src = NULL;

    return str;
}

/* qlsdk_strtok 的线程安全版本 */
char *qlsdk_strtok_r(char *str, const char *delim, char **saveptr)
{
    char *src = *saveptr;
    char *p0 = NULL;
    const char *p1 = NULL;

    if (NULL != str) {
        src = str;
    } else if (NULL != src) {
        str = src;
    } else {
        return NULL;
    }

    for (p0 = str; '\0' != *p0; ++p0) {
        for (p1 = delim; '\0' != *p1; ++p1 ) {
            if (*p0 == *p1) {
                *p0 = '\0';
                *saveptr = p0 + 1;

                return str;
            }
        }
    }
    *saveptr = NULL;

    return str;
}

int qlsdk_strtok_count(const char *str, const char *delim)
{
    if (NULL == str || NULL == delim)
        return -EINVAL;

    const char *p0 = NULL;
    const char *p1 = NULL;
    int n = 1;

    for (p0 = str; '\0' != *p0; ++p0) {
        for (p1 = (char *)delim; '\0' != *p1; ++p1) {
            if (*p0 == *p1) {
                n++;
                break;
            }
        }
    }

    return n;
}

int qlsdk_atoi(const char *str, size_t cnt)
{
    char *buf = qlmalloc(cnt+1);
    int ret = 0;

    if (NULL != buf) {
        memcpy(buf, str, cnt);
        *(buf + cnt) = '\0';
        ret = atoi(buf);
        qlfree(buf);
    }

    return ret;
}

long int qlsdk_strtol(const char *str, size_t cnt, int base)
{
    char *buf = qlmalloc(cnt+1);
    long int ret = 0;

    if (NULL != buf) {
        memcpy(buf, str, cnt);
        *(buf + cnt) = '\0';
        ret = strtol(buf, NULL, base);
        qlfree(buf);
    }

    return ret;
}

int qlsdk_strrepl(char *str, const char *victim, const char c)
{
    int i, j;

    if ((NULL == str) ||
        (NULL == victim))
        return -EINVAL;

    for (i=0; i < strlen(str); i++) {
        for (j=0; j < strlen(victim); j++) {
            if (*(str+i) == *(victim+j)) {
                *(str+i) = c;
                break;
            }
        }
    }

    return 0;
}

int qlsdk_strtrim(char *str, const char *trim)
{
    char *start = NULL;
    int i, j;

    if ((NULL == str) ||
        (NULL == trim))
        return -EINVAL;

    for (i=0; i < strlen(str); i++) {
        for (j=0; j < strlen(trim); j++) {
            if (*(str+i) == *(trim+j)) {
                start = str+i+1;
                break;
            }
        }
        if (!(j < strlen(trim)))
            break;
    }

    for (i = strlen(str)-1; i>=0; i--) {
        for (j=0; j < strlen(trim); j++) {
            if (*(str+i) == *(trim+j)) {
                *(str+i) = '\0';
                break;
            }
        }
        if (!(j < strlen(trim)))
            break;
    }

    if (NULL != start)
        strcpy(str, start);

    return 0;
}

bool qlsdk_strtail(const char *str, char tail)
{
    size_t len;

    if (NULL == str)
        return false;

    len = strlen(str);
    if (0 == len)
        return false;

    if (tail == str[len-1])
        return true;

    return false;
}

void qlsdk_strlf_add(char *str)
{
    if (NULL == str)
        return;

    size_t str_len = strlen(str);
    if (0 == str_len)
        goto _end;

    if (str_len > 1){
        if ('\r' == str[str_len - 2] && '\n' == str[str_len - 1])
            return;
    }

    if (str[str_len - 1] == '\r' || str[str_len -1] == '\n')
        str_len--;

_end:
    str[str_len] = '\r';
    str[str_len + 1] = '\n';
    str[str_len + 2] = '\0';
}

/*
 * @brief  从字符串的末尾开始倒序查找字符
 * @param  str: [I], 待查找的字符串;
 *         delim: [I], 待查找的字符, 字符串格式, 查找到任一字符终止.
 * @note   从 str 末尾开始倒序查找 delim 字符串中任意一个字符.
 * @retval NULL:无对应字符, others:查找到的字符的指针.
 */
char *qlsdk_strchr_last(const char *str, const char *delim)
{
    if (NULL == str || '\0' == *str || NULL == delim || '\0' == *delim)
        return NULL;

    size_t str_len = strlen(str);
    size_t delim_len = strlen(delim);

    for(; 0 < str_len; str_len--) {
        for (int j = 0; j < delim_len; j++)
            if (str[str_len - 1] == delim[j])
                goto _end;
    }

    return NULL;

_end:
    return (char *)&str[str_len - 1];
}

ssize_t qlsdk_memtrim(char *buf, size_t n)
{
    size_t i = 0;

    if ((0 == n) ||
        (NULL == buf))
        return -EINVAL;

    while ((i < n) &&
           (isspace(buf[i]) ||
            iscntrl(buf[i])))
        i++;
    if (0 < i) {
        n -= i;
        if (0 < n)
            memcpy(buf, buf+i, n);
    }

    return (ssize_t)n;
}

/*
 * @brief  在内存中不区分大小写查找字符串
 * @param  buf: [I], 待查找的内存数据;
 *         n: [I], 待查找的内存数据长度;
 *         str: [I], 待查找的字符串.
 * @retval NULL:内存中没有要找的字符串, others:找到的字符串的指针.
 */
char *qlsdk_memcasestr(const void *buf, size_t n, const char *str)
{
    int temp = 0;
    int i    = 0;
    int j    = 0;
    int flag = 0;
    int str_len = strlen(str);
    char *p  = (char *)buf;

    if (NULL == buf || NULL == str || 0 >= n)
        return NULL;

    if ('\0' == *str)                           //判断str首字母是否为结束符；
        flag = 1;

    for (; (i < n) && (n - i >= str_len); i++, j = 0) {
        temp = i;

        while ((p[i] + low_to_upp(p[i])) == (str[j] + low_to_upp(str[j]))) {
            if( 1 == flag || '\0' == str[j + 1])
                return &p[temp];

            i++;
            j++;

            if (i >= n)
                return NULL;
        }
        i = temp;
    }

    return NULL;
}

/*
 * @brief  判断内存buf1是否包含buf2.
 * @note   1.本函数与 strstr 类似, 仅查找首个匹配的内存地址.
 * @param  buf1: [I], 被查找的目标内存"父串";
 *         n1: [I], 待查找的"父串"数据长度;
 *         buf2: [I], 要查找的内存对象"子串";
 *         n1: [I], 待查找的"子串"数据长度;
 * @retval NULL:内存中没有要找的"子串", others:找到的"子串"的指针.
 */
void *qlsdk_memsrch(const void *buf1, size_t n1, const void *buf2, size_t n2)
{
    int temp = 0;
    int i    = 0;
    int j    = 0;
    unsigned char *p1  = (unsigned char *)buf1;
    unsigned char *p2  = (unsigned char *)buf2;

    if (NULL == p1 || NULL == p2 || 0 >= n1 || 0 >= n2)
        return NULL;

    for (; i < n1; i++, j = 0) {
        temp = i;

        while (p1[i] == p2[j]) {
            if(n2 <= j + 1)
                return &p1[temp];

            i++;
            j++;

            if (i >= n1)
                return NULL;
        }
        i = temp;
    }

    return NULL;
}

char *qlsdk_memsrch_each(const void *buf, size_t n, const char *str)
{
    char *p = (char *)buf;
    size_t len = strlen(str);
    size_t i, j;

    for (i=0; i < n; i++) {
        for (j=0; j < len; j++) {
            if (p[i] == str[j])
                return &p[i];
        }
    }

    return NULL;
}

char *qlsdk_memstr(const void *buf, size_t n, const char *str)
{
    size_t len = strlen(str);
    size_t i;

    if ((n < len) ||
        (NULL == buf) || (0 == n) ||
        (NULL == str) || (0 == len))
        return NULL;

    for (i=0; i < n; i++) {
        if ((*((char *)buf+i) == *str) &&
            (!memcmp((char *)buf+i, str, len)) &&
            (i+len <= n))
            return (char *)buf+i;
    }

    return NULL;
}

char *qlsdk_memstr_last(const void *buf, size_t n, const char *str)
{
    size_t len = strlen(str);
    char *last = NULL;
    size_t i;

    if ((n < len) ||
        (NULL == buf) || (0 == n) ||
        (NULL == str) || (0 == len))
        return NULL;

    for (i=0; i < n; i++) {
        if ((*((char *)buf+i) == *str) &&
            (!memcmp((char *)buf+i, str, len)) &&
            (i+len <= n))
            last = (char *)buf+i;
    }

    return last;
}

/* CRC-16, 0x8005, Modbus */
unsigned short qlsdk_crc16_modbus(const unsigned char *buf, unsigned int cnt)
{
    uint16_t crc = 0xFFFF;
    int i;

    if (!cnt)
        return crc;

    while (cnt--) {
        crc ^= *buf++;
        for (i=0; i<8; i++) {
            if (crc & 0x0001)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc = crc >> 1;
        }
    }

    return crc;
}

/* CRC-CCITT(0xFFFF), 0x1021 */
unsigned short qlsdk_crc16_ccitt(const unsigned char *buf, unsigned int cnt)
{
    unsigned short crc = 0xFFFF;
    int i;

    while (cnt--) {
        crc ^= (unsigned short) *buf++ << 8;

        for(i = 0; i < 8; ++i)
            crc = crc << 1 ^ (crc & 0x8000 ? 0x1021 : 0);
    }

    return (crc & 0xFFFF);
}

int qlsdk_difftime(time_t new, time_t last)
{
    return (int)difftime(new, last);
}

int qlsdk_chars_to_bytes(char *chars, size_t len, unsigned char *bytes)
{
    if (0 != len % 2)
        return -EINVAL;

    for (int i = 0; i < len; i++) {
        if ('0' <= *(chars + i) && '9' >= *(chars + i))
            continue;

        if ('a' <= *(chars + i) && 'z' >= *(chars + i))
            continue;

        if ('A' <= *(chars + i) && 'Z' >= *(chars + i))
            continue;

        return -EINVAL;
    }

    for (int i = 0, j = 0; i < len; i += 2, j++) {
        if ('0' <= *(chars + i) && '9' >= *(chars + i))
            *(bytes + j) = (*(chars + i) - 0x30) << 4;
        else
            *(bytes + j) = (*(chars + i) + 10 - 0x41) << 4;

        if ('0' <= *(chars + i + 1) && '9' >= *(chars + i + 1))
            *(bytes + j) |= (*(chars + i + 1) - 0x30);
        else if ('A' <= *(chars + i + 1) && 'Z' >= *(chars + i + 1))
            *(bytes + j) |= (*(chars + i + 1) + 10 - 0x41);
        else
            *(bytes + j) |= (*(chars + i + 1) + 10 - 0x61);
    }

    return 0;
}

/* 把字节转换为2字符, 如 0xA8=>'A''8', 0x7C=>'7''C' */
int qlsdk_byte_to_chars(unsigned char byte, char *char1, char *char2)
{
    unsigned char c1, c2;

    c1 = byte >> 4;
    c2 = byte & 0x0F;

    /* c1 */
    if (9 >= c1) /* 0~9 */
        *char1 = c1 + 0x30; /* 0x30<=>'0' */
    else if ((10 <= c1) && (15 >= c1)) /* A~F */
        *char1 = c1 - 10 + 0x41; /* 0x41<=>'A' */
    else
        return -EINVAL;

    /* c2 */
    if (9 >= c2) /* 0~9 */
        *char2 = c2 + 0x30; /* 0x30<=>'0' */
    else if ((10 <= c2) && (15 >= c2)) /* A~F */
        *char2 = c2 - 10 + 0x41; /* 0x41<=>'A' */
    else
        return -EINVAL;

    return 0;
}

/* 把字节转换为字符 */
int qlsdk_bytes_to_chars(unsigned char *bytes, size_t count, char *chars)
{
    int rc;
    int i;

    for (i=0; i < count; i++) {
        if (0 > (rc = qlsdk_byte_to_chars(*bytes, chars, chars+1)))
            return rc;
        bytes += 1;
        chars += 2;
    }

    return 0;
}

bool qlsdk_is_all_numeric(const char *buf, size_t cnt)
{
    int i;

    if ((NULL == buf) || (!cnt))
        return false;

    for (i=0; i < cnt; i++) {
        if (('0' > buf[i]) || ('9' < buf[i]))
            return false;
    }

    return true;
}

unsigned int qlsdk_char_amount(const char *buf, size_t cnt, const char c)
{
    unsigned int amount = 0;
    int i;

    for (i=0; i < cnt; i++) {
        if (c == buf[i])
            amount++;
    }

    return amount;
}

#if 0

int qlsdk_basename(const char *path, char *basename)
{
    char sep = _qlsdk->cfg->path_separator;
    char *src = (char *)path;
    char *p = NULL;
    char *end;

    if ((NULL == path) ||
        (NULL == basename))
        return -EINVAL;

    if ((1 == strlen(path)) &&
        (sep == *path)) {
        strcpy(basename, path);
        return 0;
    }

    while (NULL != (src = strchr(src, sep)))
        p = ++src;

    if (NULL == p) { /* all */
        strcpy(basename, path);
    } else if ('\0' == *p) { /* end is '/' */
        p -= 2;
        while ((sep == *p) && (path < p))
            p--;
        end = p;
        while ((sep != *p) && (path < p))
            p--;
        if (sep == *p)
            p++;
        memcpy(basename, p, end - p + 1);
        *(basename + (end - p + 1)) = '\0';
    } else {
        strcpy(basename, p);
    }

    return 0;
}

int qlsdk_dirname(const char *path, char *dirname)
{
    char sep = _qlsdk->cfg->path_separator;
    char *src = (char *)path;
    char *p = NULL;
    char *end;

    if ((NULL == path) ||
        (NULL == dirname))
        return -EINVAL;

    if ((1 == strlen(path)) &&
        (sep == *path)) {
        strcpy(dirname, path);
        return 0;
    }

    /* first is '/' and end is '/' */
    if ((sep == *path) &&
        (sep == *(path + strlen(path) - 1))) {
        *(dirname+0) = sep;
        *(dirname+1) = '\0';
        return 0;
    }

    /* end is '/' and only one '/' */
    if ((sep == *(path + strlen(path) - 1)) &&
        (1 == qlsdk_char_amount(path, strlen(path), sep))) {
        strcpy(dirname, ".");
        return 0;
    }

    while (NULL != (src = strchr(src, sep)))
        p = src++;

    if (NULL == p) { /* all */
        strcpy(dirname, ".");
    } else {
        while ((sep == *p) && (path < p))
            p--;
        end = p;
        memcpy(dirname, path, end - path + 1);
        *(dirname + (end - path + 1)) = '\0';
    }

    return 0;
}
#endif
void qlsdk_tm_wday_update(struct tm *tm)
{
    unsigned short y,c,m;

    y = tm->tm_year + 1900;                         /* since 1900 */

    if (0 == tm->tm_mon || 1 == tm->tm_mon) {       /* January or February */
        m = tm->tm_mon + 13;                        /* +12 and +1, tm->tm_mon:0~11 */
        y -= 1;
    } else {
        m = tm->tm_mon + 1;
    }

    c = y/100;                                      /* 世纪 -1 */
    y = y%100;                                      /* 年份后两位 */

    tm->tm_wday = (y + (y/4) + (c/4) - (2*c) + (26*(m+1)/10) + tm->tm_mday - 1)%7;
}


/* 计算俩经纬坐标点的距离, Km */
double qlsdk_gnss_calculate_distance(double lat1, double lng1, double lat2, double lng2)
{
    double s;
    double dA = angel_to_rad(lat1 - lat2);
    double dB = angel_to_rad(lng1 - lng2);
    s = 2 * asin(sqrt(pow(sin(dA / 2), 2) + cos(angel_to_rad(lat1))*cos(angel_to_rad(lat2))
        * pow(sin(dB / 2), 2)));
    s = s * EARTH_RADIUS;
    return s;
}

/* 查找字符串第一个不为空格的指针(跳过前面的空格符) */
static const char *skip_front_space(const char *str)
{
    while ('\0' != *str) {
        if (' ' != *str)
            return str;
        str++;
    }

    return NULL;
}

/*   @brief  从字符串(一般为AT指令)中以第一个':'为起始字符, 以','为分隔符查找对应的字段
 *   @note   自动跳过找到的字段前面的空格;'\r''\n'也作为分隔符使用.
 *   @param  str: [I], 待查找字符串;
 *           index: [I], 待查找字段号, 从0开始;
 *           size: [O], 查找到的字段长度, 仅当函数返回非空时有效; */
const char *qlsdk_at_field_search(const char *str, int index, size_t *size)
{
    const char *start = NULL;
    int idx = 0;
    size_t len;

    str = strchr(str, ':');
    if (NULL == str)
        return NULL;

    start = skip_front_space(++str);

    for(;;) {
        if ((',' == *str) ||
                   ('\0' == *str) ||
                   (is_line_feed_char(*str) && (NULL != start))) {
            if (idx == index) {
                if (NULL != start) {
                    len = str - start;
                    if (NULL != size)
                        *size = len;
                    if (!len)
                        return NULL;
                    else
                        return start;
                } else {
                    return NULL;
                }
            } else if ('\0' == *str) {
                return NULL;
            } else {
                idx++;
                start = skip_front_space(++str);
            }
        } else {
            str++;
        }
    }
}
